﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using FairyGUI.Utils;

namespace FairyGUI
{
	public class AsyncCreationHelper
	{
		public static void CreateObject(PackageItem item, UIPackage.CreateObjectCallback callback)
		{
			Timers.inst.StartCoroutine(_CreateObject(item, callback));
		}

		static IEnumerator _CreateObject(PackageItem item, UIPackage.CreateObjectCallback callback)
		{
			Stats.LatestObjectCreation = 0;
			Stats.LatestGraphicsCreation = 0;

			float frameTime = UIConfig.frameTimeForAsyncUIConstruction;

			List<DisplayListItem> itemList = new List<DisplayListItem>();
			DisplayListItem di = new DisplayListItem(item, ObjectType.Component);
			di.childCount = CollectComponentChildren(item, itemList);
			itemList.Add(di);

			int cnt = itemList.Count;
			List<GObject> objectPool = new List<GObject>(cnt);
			GObject obj;
			float t = Time.realtimeSinceStartup;
			bool alreadyNextFrame = false;

			for (int i = 0; i < cnt; i++)
			{
				di = itemList[i];
				if (di.packageItem != null)
				{
					obj = UIObjectFactory.NewObject(di.packageItem);
					obj.packageItem = di.packageItem;
					objectPool.Add(obj);

					UIPackage._constructing++;
					if (di.packageItem.type == PackageItemType.Component)
					{
						int poolStart = objectPool.Count - di.childCount - 1;

						((GComponent)obj).ConstructFromResource(objectPool, poolStart);

						objectPool.RemoveRange(poolStart, di.childCount);
					}
					else
					{
						obj.ConstructFromResource();
					}
					UIPackage._constructing--;
				}
				else
				{
					obj = UIObjectFactory.NewObject(di.type);
					objectPool.Add(obj);

					if (di.type == ObjectType.List && di.listItemCount > 0)
					{
						int poolStart = objectPool.Count - di.listItemCount - 1;
						for (int k = 0; k < di.listItemCount; k++) //把他们都放到pool里，这样GList在创建时就不需要创建对象了
							((GList)obj).itemPool.ReturnObject(objectPool[k + poolStart]);
						objectPool.RemoveRange(poolStart, di.listItemCount);
					}
				}

				if ((i % 5 == 0) && Time.realtimeSinceStartup - t >= frameTime)
				{
					yield return null;
					t = Time.realtimeSinceStartup;
					alreadyNextFrame = true;
				}
			}

			if (!alreadyNextFrame) //强制至至少下一帧才调用callback，避免调用者逻辑出错
				yield return null;

			callback(objectPool[0]);
		}

		/// <summary>
		/// 收集创建目标对象所需的所有类型信息
		/// </summary>
		/// <param name="item"></param>
		/// <param name="list"></param>
		static int CollectComponentChildren(PackageItem item, List<DisplayListItem> list)
		{
			ByteBuffer buffer = item.rawData;
			buffer.Seek(0, 2);

			int dcnt = buffer.ReadShort();
			DisplayListItem di;
			PackageItem pi;
			for (int i = 0; i < dcnt; i++)
			{
				int dataLen = buffer.ReadShort();
				int curPos = buffer.position;

				buffer.Seek(curPos, 0);

				ObjectType type = (ObjectType)buffer.ReadByte();
				string src = buffer.ReadS();
				string pkgId = buffer.ReadS();

				buffer.position = curPos;

				if (src != null)
				{
					UIPackage pkg;
					if (pkgId != null)
						pkg = UIPackage.GetById(pkgId);
					else
						pkg = item.owner;

					pi = pkg != null ? pkg.GetItem(src) : null;
					di = new DisplayListItem(pi, type);

					if (pi != null && pi.type == PackageItemType.Component)
						di.childCount = CollectComponentChildren(pi, list);
				}
				else
				{
					di = new DisplayListItem(null, type);
					if (type == ObjectType.List) //list
						di.listItemCount = CollectListChildren(buffer, list);
				}

				list.Add(di);
				buffer.position = curPos + dataLen;
			}

			return dcnt;
		}

		static int CollectListChildren(ByteBuffer buffer, List<DisplayListItem> list)
		{
			buffer.Seek(buffer.position, 8);

			string defaultItem = buffer.ReadS();
			int listItemCount = 0;
			int itemCount = buffer.ReadShort();
			for (int i = 0; i < itemCount; i++)
			{
				int nextPos = buffer.ReadShort();
				nextPos += buffer.position;

				string url = buffer.ReadS();
				if (url == null)
					url = defaultItem;
				if (!string.IsNullOrEmpty(url))
				{
					PackageItem pi = UIPackage.GetItemByURL(url);
					if (pi != null)
					{
						DisplayListItem di = new DisplayListItem(pi, pi.objectType);
						if (pi.type == PackageItemType.Component)
							di.childCount = CollectComponentChildren(pi, list);

						list.Add(di);
						listItemCount++;
					}
				}
				buffer.position = nextPos;
			}

			return listItemCount;
		}

		/// <summary>
		/// 
		/// </summary>
		class DisplayListItem
		{
			public PackageItem packageItem;
			public ObjectType type;
			public int childCount;
			public int listItemCount;

			public DisplayListItem(PackageItem pi, ObjectType type)
			{
				this.packageItem = pi;
				this.type = type;
			}
		}
	}
}
